from deviceServerBaseQueue1 import C_queueServer

'''
应用业务逻辑处理
通过建立tcp服务端，设备与服务端连接：控制类设备需要保持长连接，在长连接中在实施获取设备状态并且需要通过pika组件和Django建立通信保持和微信小程序的同步
需要和Django端同步，使用pika的消息队列组件通信
伪代码逻辑：

    初始化
    创建队列现成
    创建tcp线程 
        在init中将socket进行初始化
    接收连接等待
        如果有设备连接
            创建数据接收线程
                等待初始化数据的接收
                    如果没有接收到数据，时间到达后断开连接
    创建数据发送线程
        等待消息队列的消息
            如果接收到了消息队列的消息，将消息中的设备ID匹配到对应的socke端口上，再将数据对应转发出去
'''
import socket  # 导入 socket 模块
from threading import Thread
import time
import json

import pika
from queue import Queue

import operator # 用于比较数据的不同 适用任何数据
# https://blog.csdn.net/linxinfa/article/details/104001443
# 比较元素 https://blog.csdn.net/qq_24918869/article/details/52175886
# queueu https://www.cnblogs.com/lincappu/p/12890761.html

from deviceServerApi import C_communicatWeb
from deviceServerWidgetDataProcess import C_deviceDataProcess
from deviceServerWidgetDataBase import C_deviceDataBase

class C_deviceServer:
    socket_server = None
    socket_server_ADDRESS = None
    socket_serverlisten_Num = None

    def __init__(self):
        self.obj_deviceList = C_deviceDataBase()
        self.obj_deviceDataProcess =  C_deviceDataProcess()

        self.listen_Num = 0 # 最大监听数
        self.socket_server_ADDRESS = ('0.0.0.0', 8883)  # 绑定地址
        self.obj_socketServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  
        self.obj_socketServer.bind(self.socket_server_ADDRESS)
        self.obj_socketServer.listen(self.listen_Num)  # 最大等待数（有很多人理解为最大连接数，其实是错误的）
        print("C_deviceServer:初始化服务端")

    def run(self):
        thread_accept = Thread(target = self.accept_client_task_run) # 设备发送数据给django
        thread_accept.setDaemon(True) # 设置成守护线程
        thread_accept.start()

        thread_sand = Thread(target = self.sand_device_task_run) # 设备发送数据给django
        thread_sand.setDaemon(True) # 设置成守护线程
        thread_sand.start()
    
    def accept_client_task_run(self):
        """
        接收新连接
        """
        print("C_deviceServer:打开监听准备连接")
        while True:
            '''
            通过阻塞等待，表示已经接收到了一个新的TCP连接，那么可以向下创建新的接收和发送数据
            '''
            client, info = self.obj_socketServer.accept()  # 阻塞，等待客户端连接
                      
            thread = Thread(target = self.message_handle_rev, args=(client, info)) # 设备发送数据给django
            thread.setDaemon(True) # 设置成守护线程
            thread.start()

    def message_handle_rev(self, client, info):
        """
        等待设备连接，根据初始化的数据内容进行设备的初始化
        连接到设备后首先接收设备发送过来的基础设备信息和上线条件
        """
        client.settimeout(10.0)
        try:
            clientMessage = client.recv(1024)
            print("在阻塞中接收到值：", clientMessage)
            #if(len(bytes) == 0): # 连接有误
            #    return False
            # 接收到数据以后对数据进行校验
            device_ID , value = self.obj_deviceDataProcess.device_init_data_get(clientMessage)
            print("接收到数据以后对数据进行校验", device_ID,value)
            '''
            将一个设备的信息通过类储存起来
            '''
            print("C_deviceServer:新创建了一个客户端", client)
            aDevice = { 
                "device_ID":device_ID, 
                "socket_class":client, 
                "device_Online":True, 
                "device_recv_falg": self.obj_deviceList.FLAG_GET_MES_NO
                }
            self.obj_deviceList.device_add(aDevice)
            for i in self.obj_deviceList.device_list :
                print("C_deviceServer：设备列表信息：",i)

            '''
            心跳处理
            '''
            client.settimeout(None)
            while True:
                '''
                阻塞等待设备发送数据过来 接受到数据后对二进制protobuf数据解析
                解析成功发送给web服务器
                解析失败返回给设备ERROR字符串
                '''
                bytes = client.recv(1024)
                if(len(bytes)):
                    try:
                        print("device server recv:", bytes)
                        jsonStr = self.obj_deviceDataProcess.device_protobuf_set(protoData = bytes)
                        if jsonStr != False: # 如果数据转换成功
                            client.send("OK".encode(encoding='gb2312')) # 发送错误信息返回给设备
                            if self.obj_deviceList.device_get_recvFalg(Device_ID=device_ID) == self.obj_deviceList.FLAG_GET_MES_YES : # 获取信号
                                print("message_handle_rev: 检测到了微信小程序的post请求 需要把数据转发到web")
                                self.communicatWeb_sand(Datastr=jsonStr, QueueName="1381234432110") # 将解析出来的json发送给web
                                self.obj_deviceList.device_recvFlah_rest(Device_ID=device_ID)
                            else:
                                #print("message_handle_rev do Nothing")
                                pass
                        else: # 如果数据转换失败
                            print("错误: message_handle_rev 数据转换错误")
                            client.send("ERROR".encode(encoding='gb2312')) # 发送错误信息返回给设备
                    except:
                        print("C_deviceServer:message_handle_rev:接收设备传过来的数据解析失败 数据错误")
                else:
                    print("message_handle_rev: tcp断开了")  
                    client.close()
                    break
        except:
            print("没有接收到值 退出线程 关闭客户端连接")
            client.settimeout(None)
            client.close() 
        return

    def sand_device_task_run(self):
        communicatWeb = C_communicatWeb()
        communicatWeb.recv_init() # 将从socket接收到的数据通过队列发送到上位机Django
        print("sand_device_task_run 发送端初始化")
        while True:
            try:
                jsonStr = communicatWeb.recv_get_data(timeout=2) # 阻塞方式接收获取数据
                print("sand_device_task_run:收获取数据jsonStr:", jsonStr)
                dataDict = json.loads(jsonStr)
                Payload = dataDict["Payload"]
                aDevice = self.obj_deviceList.device_find(Payload["Device_ID"])
                if(aDevice != False) :
                    buf = self.obj_deviceDataProcess.device_protobuf_get(Payload) # 将字典转换成二进制数据 deviceSocketObj.send("C_deviceServer: connect server successfully!".encode(encoding='utf8')) # 发送字符串
                    aDevice["socket_class"].send(buf) # 发送二进制数据
                    #print("C_deviceServer:sand_device_task_run:Payload:", Payload, buf)
                    self.obj_deviceList.device_recvFlag_set(Device_ID=Payload["Device_ID"]) # 通知数据接收线程将接收到的数据通过消息队列发送给web
                else:
                    print("C_deviceServer:sand_device_task_run:Payload:此设备不在线")
                    self.communicatWeb_sand(Datastr="ERROR_DEVICE_FIND", QueueName="1381234432110") # 将解析出来的json发送给web
            except:
                #print("C_deviceServer", "还没有数据要发送给设备")
                pass

    def communicatWeb_sand(self, Datastr, QueueName="1381234432110"):
        communicatWeb = C_communicatWeb()
        communicatWeb.sand_init()
        communicatWeb.sand_set_data(Datastr, QueueName) # # 将解析出来的json发送给web

    def heartbeat(self, times, deviceClient):
        '''
        创建设备的连接之初，需要等待设备发送自己的ID，发送完成后实现连接
        '''   
        while True:
            time.sleep(times)             

if __name__ == '__main__':
    aClass = C_deviceServer()
    aClass.run()
    
    while(1):
        time.sleep(1)